package org.jspar.route;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.jspar.Constants;
import org.jspar.model.Module;
import org.jspar.model.Net;
import org.jspar.tile.ITileContent;
import org.jspar.tile.Tile;

public class Arc implements ITileContent {
	
	private int congestion;
	private int page;
	private Object corners;
	private List<Net> nets = new ArrayList<Net>();
	private Module module;
	private int vcap;
	private int hcap;
	private List<Trail>[] trails;
	private Tile tile;

	public Arc(int width, int height, int page, Module module) {
		this.congestion = 0;
		this.corners = null;
		this.page = page;
		//this.tile = t;
		this.module = module;
		this.vcap = (page == GlobalRouting.VERT) ? width : height;
		this.hcap = (page == GlobalRouting.HORZ) ? width : height;

		this.trails = new List[Constants.HASH_SIZE];
		for (int i = 0; i < trails.length; i++)
			trails[i] = new ArrayList<Trail>();
	}

	public Arc(Tile t, int page) {
		this.congestion = 0;
		this.page = page;
		this.corners = null;
		this.tile = t;
		this.vcap = (page == GlobalRouting.VERT) ? t.width() : t.height();
		this.hcap = (page == GlobalRouting.HORZ) ? t.width() : t.height();
		this.trails = new List[Constants.HASH_SIZE];
		for (int i = 0; i < Constants.HASH_SIZE; ++i)
			this.trails[i] = null;
	}

	public Module module() {
		return module;
	}
	
	public Tile tile() {
		return tile;
	}

	public void setTile(Tile t) {
		this.tile = t;
	}
	
	public int congestion() {
		return congestion;
	}

	public void setCongestion(int i) {
		this.congestion = i;
	}

	public List<Net> nets() {
		return nets;
	}

	public int page() {
		return page;
	}

	public List<Trail> trails(int hashval) {
		return trails[hashval];
	}

	public int congestionCost() {
		double TracksUsed = (double) nets.size();
		double TracksAvail = (page == GlobalRouting.VERT) ? (double) hcap
				: (double) vcap;

		double cost = (TracksUsed / TracksAvail) * Constants.TRACK_WEIGHT
				+ congestion / TracksAvail;
		if (TracksUsed >= (int) TracksAvail - 1) {
			cost = 5 * cost + 40;
		}
		return (int) cost;
	}

	public void fullRehash() {
		for (int i = 0; i < Constants.HASH_SIZE; ++i) {
			for (Trail t : trails[i]) {
				t.computeCost();
			}
			Collections.sort(trails[i], new Comparator<Trail>() {
				@Override
				public int compare(Trail o1, Trail o2) {
					return o1.cost() - o2.cost();
				}
			});
		}

	}
	
	public void insertTrailA(Trail tr) {
		int hashval = tr.expansion().hashCode() % Constants.HASH_SIZE;

		if (tr.isValid()) {
			if (trails[hashval] != null && trails[hashval].size() > 0
					&& trails[hashval].get(0).cost() == tr.cost()) {
				if (trails[hashval].get(0).breakCostTie(tr))
					tr.incCost(1);
				else
					trails[hashval].get(0).incCost(1);
			}
			if (trails[hashval] == null)
				trails[hashval] = new ArrayList<Trail>();
			trails[hashval].add(tr);
			Collections.sort(trails[hashval]);
		}
	}

	public List<Trail> remove_trailA(Trail tr) {
		 int index = tr.expansion().hashCode() % Constants.HASH_SIZE;
		 if (trails[index] != null)
			 trails[index].remove(tr);
		 return trails[index];
	}

	public Trail getBestTrail(Expansion parentEx) {
		Trail besTrail = null;
		for (int i = 0; i < Constants.HASH_SIZE; ++i) {
			if (trails[i] != null) {
				for (Trail temp : trails[i]) {
					if (temp.meets_termination_conditions_p(parentEx, besTrail)) {
						besTrail = temp;
					}
				}
			}
		}
		return besTrail;
	}

	public Trail best_trailA(Expansion ex) {
		int index = ex.hashCode() % Constants.HASH_SIZE;
		if (trails[index] != null) {
			for (Trail t : trails[index]) {
				if (t.expansion() == ex) {
					return t;
				}
			}
		}
		return null;
	}

	public List<Trail> getTrails() {
		List<Trail> result = new ArrayList<Trail>();
		for (int i = 0; i < Constants.HASH_SIZE; ++i) {
			if (trails[i] != null) {
				result.addAll(trails[i]);
			}
		}
		return result;
	}

}
